/**
 * @file mypopen.c
 * Betriebssysteme Mypopen File.
 *
 * Beispiel 2
 *
 * @author Markus Scharitzer 1410258040 <markus.scharitzer@technikum-wien.at>
 * @author Helene Wiesinger 1410258004 <helene.wiesinger@technikum-wien.at>
 * @author Siegfried Becher 141025035 <siegfried.becher@technikum-wien.at>
 * @date 2015/04/19
 *
 * @version 1.0
 *
 * Own implementations of popen and pclose
 *
 */

/*
 * -------------------------------------------------- includes --
 */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include "mypopen.h"

/*
 * --------------------------------------------------- defines --
 */
#define READ 0
#define WRITE 1

/*
 * -------------------------------------------------- typedefs --
 */

/*
 * --------------------------------------------------- globals --
 */
static pid_t pid = -1;
static FILE *file;
static void closeFD(int fd[2]);
static void reset(void);

/*
 * ------------------------------------------------- functions --
 */

/**
 *
 * \brief mypopen function
 *
 * handles request of Main-function
 * creates pipe, checks if read or write, checks instance of process
 *
 * \param command command typed into the console
 * \param type specifies either read or write
 *
 * \return pointer to file
 *
 */
FILE *mypopen(const char *command, const char *type) {
  int fd[2];
  FILE *fp = NULL;

  if (pid > -1) {
    errno = EAGAIN;
    return NULL;
  }

  if ((*type != 'r' && *type != 'w') || type[1]) {
    errno = EINVAL;
    return NULL;
  }

  if (pipe(fd) == -1) {
    /* errno set by pipe() */
    return NULL;
  }

  pid = fork();
  switch (pid) {
    /* error */
  case -1:
    /* errno set by fork() */
    closeFD(fd);
    reset();
    exit(EXIT_FAILURE);
    break;
    
    /* child */
  case 0: 
    if (*type == 'r') {
      if(dup2(fd[WRITE], STDOUT_FILENO) == -1) {
	/* errno set by dup2() */
	reset();
	exit(EXIT_FAILURE);
      }
      (void) close(fd[WRITE]);
      (void) close(fd[READ]);
    } else {

      if(dup2(fd[READ], STDIN_FILENO) == -1 ) {
	reset();
	exit(EXIT_FAILURE);
      }
      (void) close(fd[READ]);
      (void) close(fd[WRITE]);
    }
    if (execl("/bin/sh", "sh", "-c", command, (char *)NULL) < 0) {
      /* errno set by execl() */
      reset();
      (void) closeFD(fd);
    }
    break;
  /*parent */
  default:
    break;
  }

  if (*type == 'r') {
    if ((fp = fdopen(fd[READ], type)) == NULL) {
      reset();
      (void) closeFD(fd);
    }
    (void) close(fd[WRITE]);
  } else {
    if ((fp = fdopen(fd[WRITE], type)) == NULL) {
      reset();
      (void) closeFD(fd);
    }
    (void) close(fd[READ]);
  }
  file = fp;
  return fp;
}

/**
 *
 * \brief mypclose function
 *
 * closes the pipe
 *
 * \param stream pointer to file
 *
 * \return exit status - int
 *
 */
int mypclose(FILE *stream) {
  int state = 0;
  pid_t cpid = -1;
  if (pid < 0) {
    errno = ECHILD;
    return -1;
  }
  if (file == NULL || file != stream) {
    errno = EINVAL;
    return -1;
  }
  if (pid < 0) {
    return -1;
  }

  if (fclose(stream) == EOF) {
    return -1;
  }

  while ((cpid = waitpid(pid, &state, 0)) != pid) {
    if (cpid == -1) {
      if (errno != EINTR) {
        return -1;
      }
    }
  }
  reset();
  if (WIFEXITED(state)) {
    return (WEXITSTATUS(state));
  } else {
    errno = ECHILD;
    return -1;
  }

  return state;
}

/**
 *
 * \brief closeFD function
 *
 * closes pipe
 *
 * \param fd ends of pipe
 *
 * \return void
 *
 */
static void closeFD(int fd[2]) {
  (void)close(fd[0]);
  (void)close(fd[1]);
}

/**
 *
 * \brief reset function
 *
 * resets globals file and pid
 *
 * \return void
 *
 */
static void reset(void) {
  file = NULL;
  pid = -1;
}

/*
 * ======================================================= eof ==
 */
